
/*
FILE CONCAT ADD FILE
PATH: /ft/resources/client/SortableTable.js
*/
function SortableTable(table,rowOfHead)
{
	var rThead = Element.parseSelector("thead", table);
	this.list = [];

	for (var h=0; h<rThead.length; h++)
	{
		var tbody = Element.parseSelector("+tbody",rThead[h],"first");
		if (tbody) {
			this.list.push(new _SortableTable(rThead[h],tbody,rowOfHead));
		}
	}
}
SortableTable.prototype.init = function(table,rowOfHead)
{
	return new SortableTable(table, rowOfHead);
}

function _SortableTable(tHead, tBody, rowOfHead) {
	if (tHead && tBody) {
		this.eventManager = new EventManager();
		this.init(tHead, tBody, rowOfHead);
	}
};

_SortableTable.prototype.init = function(tHead, tBody, rowOfHead) {

	this.table = tHead && tHead.parentNode
		|| this.tHead && this.tHead.parentNode
		|| this.table;

	this.thead = tHead; //Element.parseSelector("thead", this.table, "first");
	this.tbody = tBody; //Element.parseSelector("tbody", this.table, "first");

	this.colHeads = Element.parseSelector("th", this.thead);
	this.sortableColHeads = Element.parseSelector("th[" + this.ATTR_DEFAULT_SORT_DIR + "]", this.thead);
	if(this.thead.WSOD_clones) {
		for(var i = 0; i < this.thead.WSOD_clones.length; i++) {
			this.sortableColHeads = this.sortableColHeads.concat(Element.parseSelector("th[" + this.ATTR_DEFAULT_SORT_DIR + "]", this.thead.WSOD_clones[i]));
		}
	}
	this.rows = Element.parseSelector("tr", this.tbody);

	this.lastSort = -1; // necessary?

	this.rowOfHead = rowOfHead || this.rowOfHead || 0;
	this.addEventHandlers();

};

_SortableTable.prototype.ATTR_DEFAULT_SORT_DIR = "sortdir";
_SortableTable.prototype.ATTR_USE_SORT_FIELD = "usesortfield";
_SortableTable.prototype.ATTR_SORT_VALUE = "sortval";
_SortableTable.prototype.CSS_SORTED = "sorted";
_SortableTable.prototype.CSS_LEFT_ALIGN = "left";
_SortableTable.prototype.CSS_SORTED_ASC = "asc";
_SortableTable.prototype.CSS_SORTED_DESC = "desc";

_SortableTable.prototype.addEventHandlers = function() {
	if (this.sortEvents) {
		this.sortEvents.removeAllElements();
	}

	this.sortEvents = this.eventManager.add(this.sortableColHeads, "click", this._sort, this);
};

_SortableTable.prototype._sort = function(e, el) {
	var addClass, replaceClass;
	
	var getCellIndex = function(el) {
		var row = el.parentNode;
		for(var i = 0; i < row.cells.length; i++) 
			if(row.cells[i] == el) return i;
		return -1;
	};
	var getOriginalCell = function(el) {
		return getRowGroup(el).WSOD_original.rows[0].cells[getCellIndex(el)];
	};
	var getRowGroup = function(el){
		return el.parentNode.parentNode;
	};
	var getCellClones = function(el) {
		var cellIndex = getCellIndex(el);
		var clones = [], row;
		for(var i = 0; i < getRowGroup(el).WSOD_clones.length; i++) {
			row = getRowGroup(el).WSOD_clones[i].rows[0];
			if (row.cells.length > cellIndex) {
				clones.push(row.cells[cellIndex]);
			}
		}
		return clones;
	};
	
	if (getRowGroup(el).WSOD_original) {
		el = getOriginalCell(el);
	}
	
	if (el.parentNode.parentNode.WSOD_original || el.parentNode.parentNode.WSOD_clones) {
		addClass = function(el, className) {
			Element.addClass(el, className);
			Element.addClass(getCellClones(el), className);
		};
		removeClass = function(el, className) {
			Element.removeClass(el, className);
			Element.removeClass(getCellClones(el), className);
		};
		replaceClass = function(el, oldClass, newClass) {
			Element.replaceClass(el, oldClass, newClass);
			Element.replaceClass(getCellClones(el), oldClass, newClass);
		};
	} else {
		addClass = function(el, className) {
			Element.addClass(el, className);
		};
		removeClass = function(el, className) {
			Element.removeClass(el, className);
		};
		replaceClass = function(el, oldClass, newClass) {
			Element.replaceClass(el, oldClass, newClass);
		};
	}

	if(el.cellIndex == this.lastSort || Element.hasClass(el, this.CSS_SORTED)) {

		if (Element.hasClass(el, this.CSS_SORTED_ASC)) {
			replaceClass(el, this.CSS_SORTED_ASC, this.CSS_SORTED_DESC);
		} else {
			replaceClass(el, this.CSS_SORTED_DESC, this.CSS_SORTED_ASC);
		}

		this.rows.reverse();

	} else {

		for (var i=0; i<this.colHeads.length; i++) {
			removeClass(this.colHeads[i], this.CSS_SORTED);
		}

		this._sortRows(el);

		addClass(el, this.CSS_SORTED);
		// not sorted, so use default
		if (this.CSS_SORTED_ASC == el.getAttribute(this.ATTR_DEFAULT_SORT_DIR)) {
			addClass(el, this.CSS_SORTED_ASC);
		}
		else {
			addClass(el, this.CSS_SORTED_DESC);
			this.rows.reverse();
		}

	}

	this.lastSort = el.cellIndex;
	this._redrawTable();
};

_SortableTable.prototype._sortRows = function(el) {
	var sortValue;
	if ("true" == el.getAttribute(this.ATTR_USE_SORT_FIELD)) {
		sortValue = this.ATTR_SORT_VALUE;
	}

	var cellIndex = el.cellIndex;
	if (el.cellIndex > 0 && this.sortableColHeads[el.cellIndex-1] && this.sortableColHeads[el.cellIndex-1].getAttribute("colSpan")) {
		 cellIndex = el.cellIndex + (this.sortableColHeads[el.cellIndex-1].getAttribute("colSpan") - 1);
	}
	

	function sorter(a,b) {
		if (sortValue) {
			var aa = String(a.cells[cellIndex].getAttribute(sortValue)).toLowerCase();
			var bb = String(b.cells[cellIndex].getAttribute(sortValue)).toLowerCase();
		}
		else {
			var aa = String(a.cells[cellIndex].innerHTML).toLowerCase();
			var bb = String(b.cells[cellIndex].innerHTML).toLowerCase();
		}

		var numAA = parseFloat(aa);
		var numBB = parseFloat(bb);

		if (!isNaN(numAA) && !isNaN(numBB)) {
			return numAA - numBB;
		}
		else if (aa>bb) {
			return 1;
		}
		else if (aa<bb) {
			return -1;
		}

		return 0;
	}

	this.rows.sort(sorter);
};

_SortableTable.prototype._redrawTable = function() {
	Element.removeChildNodes(this.tbody);
	for (var i=0; i<this.rows.length; i++) {
		Element.addChild(this.tbody, this.rows[i]);
	}
	if(this.tbody.WSOD_clone) {
		Element.removeChildNodes(this.tbody.WSOD_clone);
		for (var i=0; i<this.rows.length; i++) {
			Element.addChild(this.tbody.WSOD_clone, this.rows[i].WSOD_clone);
		}
	}
};

SortableTable.sortables = [];
SortableTable.initAllSortableTables = function(TableConstructor, tables) {
	TableConstructor = TableConstructor || SortableTable;
	tables = tables || Element.parseSelector("table.sortable");

	for (var i=0; i<tables.length; i++) {
		
		if (this.sortables[i]) {
			//already exists, reuse it
			var thead = Element.parseSelector("thead", tables[i], "first");
			var tbody = Element.parseSelector("tbody", tables[i], "first");
			this.sortables[i].init(thead, tbody);
		}
		else {
			this.sortables.push(new TableConstructor(tables[i]));
		}
	}
	return this.sortables;

};

GroupedSortableTable = function(table, rowOfHead) {
	rowOfHead = rowOfHead || 0;
	GroupedSortableTable.Super(this, null, [
		Element.parseSelector("thead", table)[rowOfHead], 
		Element.parseSelector("tbody", table, "first")
	]);
};
GroupedSortableTable.Extend(_SortableTable);

GroupedSortableTable.prototype.init = function(table, rowOfHead) {
	GroupedSortableTable.Super(this, "init", arguments);

	this.tbodys = Element.parseSelector("tbody", this.table);
};

GroupedSortableTable.prototype._sort = function(e, el) {
	var dir = -1;
	if (Element.hasClass(el, this.CSS_SORTED)) {
		if (Element.hasClass(el, this.CSS_SORTED_ASC)) {
			Element.replaceClass(el, this.CSS_SORTED_ASC, this.CSS_SORTED_DESC);
		} else {
			Element.replaceClass(el, this.CSS_SORTED_DESC, this.CSS_SORTED_ASC);
			dir = 1;
		}
	}
	else {
		if (this.colHeads[this.lastSort]) {
			Element.removeClass(this.colHeads[this.lastSort], this.CSS_SORTED);
		}

		Element.addClass(el, this.CSS_SORTED);

		if (this.CSS_SORTED_ASC == el.getAttribute(this.ATTR_DEFAULT_SORT_DIR)) {
			Element.addClass(el, this.CSS_SORTED_ASC);
			dir = 1;
		}
		else {
			Element.addClass(el, this.CSS_SORTED_DESC);
		}
	}

	this.lastSort = el.cellIndex;
	this._sortRows(el, dir);
};

GroupedSortableTable.prototype._sortRows = function(el, dir) {
	var sortValue;
	var sortFunction = this._sorter;

	if ("true" == el.getAttribute(this.ATTR_USE_SORT_FIELD)) {
		sortValue = this.ATTR_SORT_VALUE;
	}

	var groupSortRows = [];
	for (var i=0; i<this.tbodys.length; i++) {
		var rows = Element.parseSelector("tr", this.tbodys[i]);
		if (rows.length) {

			function sorter(a, b) {
				return sortFunction(rows, el, sortValue, dir, true, a, b);
			};
			rows = rows.sort(sorter);


			Element.removeChildNodes(this.tbodys[i]);
			for (var j=0; j<rows.length; j++) {
				Element.addChild(this.tbodys[i], rows[j]);
			}
			groupSortRows.push(rows[0]);
		}
	}

	function sortGroups(a, b) {
		return sortFunction(null, el, sortValue, dir, false, a, b);
	};
	groupSortRows = groupSortRows.sort(sortGroups);

	for (var i=0; i<groupSortRows.length; i++) {
		var tbody = groupSortRows[i].parentNode;
		Element.remove(tbody);
		Element.addChild(this.table, tbody);
	}

};

GroupedSortableTable.prototype._sorter = function(rows, el, sortValue, dir, preserveFirst, a, b) {
	if (preserveFirst) {
		if (rows[0] == a) {
			return -1;
		}
		else if (rows[0] == b) {
			return 1;
		}
	}

	if (sortValue) {
		var aa = String(a.cells[el.cellIndex].getAttribute(sortValue)).toLowerCase();
		var bb = String(b.cells[el.cellIndex].getAttribute(sortValue)).toLowerCase();
	}
	else {
		var aa = String(a.cells[el.cellIndex].innerHTML).toLowerCase();
		var bb = String(b.cells[el.cellIndex].innerHTML).toLowerCase();
	}

	var numAA = parseFloat(aa);
	var numBB = parseFloat(bb);

	if (!isNaN(numAA) && !isNaN(numBB)) {
		return (numAA - numBB) * dir;
	}
	else if (aa>bb) {
		return 1 * dir;
	}
	else if (aa<bb) {
		return -1 * dir;
	}

	return 0;
};

GroupedSortableTable.prototype._redrawTable = function() {
};

/*
FILE CONCAT ADD FILE
PATH: /ft/resources/client/markets/ResearchDataArchive.js
*/
ResearchDataArchive = function() {
	this.reports = [];
}

ResearchDataArchive.prototype.REPORT_TYPE_ALL = "ALL";
ResearchDataArchive.prototype.CSS_HIDDEN = "wsodHidden";

ResearchDataArchive.prototype.init = function() {
	this.REPORT = this.REPORT ? this.getSerializer().deserialize(this.REPORT) : {};

	if (Element.get("categorySelectRDA")) {
		this.getCategoryFlyout();
	}
	if (Element.get("reportSelectRDA")) {
		this.getReportFlyout();
		this.updateReports();
	}

	if (this.getCalendarContainer()) {
		this.getEventManager().add(this.getCalendarControls(), "click", this.pageCalendar, this);
	}
}

ResearchDataArchive.prototype.getCalendarContainer = function() {
	var calendarContainer = Element.parseSelector("div.calendarContainer", "researchDataArchiveModule", "first");
	var calendarControls = Element.parseSelector("div.calendarControls a", calendarContainer);
	this.getCalendarControls = function() {
		return calendarControls;
	}
	
	this.getCalendarContainer = function() {
		return calendarContainer
	}
	
	return this.getCalendarContainer();
}

ResearchDataArchive.prototype.getCategories = function() {
	var categories = [];
	for (var i in this.REPORT.CATEGORIES) {
		categories.push({label: this.REPORT.CATEGORIES[i].name, value: this.REPORT.CATEGORIES[i].code});
	}
	
	return categories;
};

ResearchDataArchive.prototype.getReports = function() {
	var reports = [];
	for (var i in this.REPORT.TYPES) {
		reports.push({label: this.REPORT.TYPES[i].title, value: this.REPORT.TYPES[i].code, css: this.REPORT.TYPES[i].category});
	}
	
	return reports;
};

ResearchDataArchive.prototype.getCategoryFlyout = function() {
	var flyout = new Flyout();
	
	flyout.setParent(Element.get("wsod"));
	flyout.addTarget(Element.get("categorySelectRDA"));
	flyout.setData(this.getCategories());
	flyout.onSelect().addListener({
		context: this,
		handler: this.selectCategory
	});	
	
	var flyoutLabel = Element.get("categorySelectRDA").firstChild;
	var flyoutControls = Element.parseSelector("ul li", flyout.getFrame());
	
	this.getCategoryFlyoutLabel = function() {
		return flyoutLabel;
	}
	
	this.getCategoryFlyoutControls = function() {
		return flyoutControls;
	}
	
	this.getCategoryFlyout = function() {
		return flyout;
	}
	
	return this.getCategoryFlyout();
}

ResearchDataArchive.prototype.getReportFlyout = function() {
	var flyout = new Flyout();
	
	flyout.setParent(Element.get("wsod"));
	flyout.addTarget(Element.get("reportSelectRDA"));
	flyout.setData(this.getReports());
	flyout.onSelect().addListener({
		context: this,
		handler: this.selectReport
	});
	
	var flyoutLabel = Element.get("reportSelectRDA").firstChild;
	var flyoutControls = Element.parseSelector("ul li", flyout.getFrame());

	this.getReportFlyoutLabel = function() {
		return flyoutLabel;
	}
	
	this.getReportFlyoutControls = function() {
		return flyoutControls;
	}
	
	this.getReportFlyout = function() {
		return flyout;
	}
	
	return this.getReportFlyout();
}

ResearchDataArchive.prototype.selectCategory = function(e, item) {
	Element.setHTML(this.getCategoryFlyoutLabel(), item.getLabel());
	this.updateReports(item.getValue());
	
	//set report back to default
	var reportTitle = Element.parseSelector("div", "reportSelectRDA");
	Element.setHTML(reportTitle, "- Select Report -");
	
	//remove report link
	var reportLink = Element.parseSelector("div.reportLink a", "researchDataArchiveModule", "first")
	
	if(reportLink == null){
		return;
	}else{
		Element.remove(reportLink);
	}
}

ResearchDataArchive.prototype.updateReports = function(val) {
	var flyoutControls = this.getReportFlyoutControls();
	
	for (var i = 0; i < flyoutControls.length; i++) {
		if ((!Element.hasClass(flyoutControls[i], val) && !Element.hasClass(flyoutControls[i], this.REPORT_TYPE_ALL)) || !val) {
			Element.addClass(flyoutControls[i], this.CSS_HIDDEN);
		} else {
			Element.removeClass(flyoutControls[i], this.CSS_HIDDEN);
		}
	}
}

ResearchDataArchive.prototype.selectReport = function(e, item) {
	Element.setHTML(this.getReportFlyoutLabel(), item.getLabel());
	
	this.setReportType(item.getValue());
	
	if (this.getCalendarContainer()) {
		Element.removeClass(this.getCalendarContainer(), "inactive");
		this.loadCalendar();
	} else {
		this.loadReport();
	}
}

ResearchDataArchive.prototype.drawReportLink = function(cb) {
	var data = this.getSerializer().deserialize(cb.getResult());
	var reportLink = Element.create("a", {"target":"_blank", "href":"/ft/markets/reports/FTReport.asp?dockey=" + data[0].DocumentTag + "-" + data[0].VersionTag}, "View Report", this.getReportLinkContainer());
	
	if (data && data.length) {
		reportLink
	} else {
		alert("Report of the requested type is unavailable at this time.");
	}
	
}

ResearchDataArchive.prototype.getReportLinkContainer = function() {
	var reportLinkContainer = Element.parseSelector("div.reportLink", "researchDataArchiveModule", "first");
	
	this.getReportLinkContainer = function() {
		return reportLinkContainer;
	}
	
	return this.getReportLinkContainer();
}

ResearchDataArchive.prototype.pageCalendar = function(e, el) {
	var month = Number(el.getAttribute("month"));
	var year = Number(el.getAttribute("year"));
	var today = new Date();
	

	if (year > today.getFullYear() || (year == today.getFullYear() && month > today.getMonth())){ 
		return; 
	}
	
	
	
	this.setDate((month + 1) + "/1/" + year);
	
	this.loadCalendar();
}

ResearchDataArchive.prototype.loadCalendar = function() {
	this.getContentBuffer().abortRequests();
	this.getContentBuffer().load({
		url: "/ft/resources/buffer/getResearchDataArchiveCalendar.asp",
		contentType: "text/html",
		data: {
			targetDate: this.getDate(),
			reportType: this.getReportType()
		},
		onload: this.drawCalendar,
		context: this
	});
}

ResearchDataArchive.prototype.loadReport = function() {
	Element.removeChildNodes(this.getReportLinkContainer());

	this.getContentBuffer().abortRequests();
	this.getContentBuffer().load({
		url: "/ft/resources/buffer/getResearchDataArchiveRecentReport.asp",
		contentType: "text/javascript",
		preventEval: true,
		data: {
			reportType: this.getReportType()
		},
		onload: this.drawReportLink,
		context: this
	});
}

ResearchDataArchive.prototype.drawCalendar = function(cb) {
	Element.setHTML(this.getCalendarContainer(), cb.getResult());
	
	this.getEventManager().add(Element.parseSelector("a[year]", this.getCalendarContainer()), "click", this.pageCalendar, this);
}

ResearchDataArchive.prototype.getReportType = function() { return ""; }
ResearchDataArchive.prototype.setReportType = function(r) {
	this.getReportType = function() {
		return r;
	}
}

ResearchDataArchive.prototype.getDate = function() {
	var el = Element.parseSelector("div.calendarCurrentDate", this.getCalendarContainer(), "first");
	
	
	var month = Number(el.getAttribute("month"));
	var year = Number(el.getAttribute("year"));
	var today = new Date();

	if (year > today.getFullYear() || (year == today.getFullYear() && month > today.getMonth())) { return ""; }
	
	var d = (month + 1) + "/1/" + year;
	
	return d;
}

ResearchDataArchive.prototype.setDate = function(d) {
	this.getDate = function() {
		return d;
	}
}

ResearchDataArchive.prototype.getEventManager = function() {
	var em = new EventManager();
	
	this.getEventManager = function() {
		return em;
	}
	
	return this.getEventManager();
}

ResearchDataArchive.prototype.getContentBuffer = function() {
	var cb = new ContentBuffer();
	
	this.getContentBuffer = function() {
		return cb;
	}
	
	return this.getContentBuffer();
}

ResearchDataArchive.prototype.getSerializer = function() {
	var s = new Serializer();
	
	this.getSerializer = function() {
		return s;
	}
	
	return this.getSerializer();
}
/*
FILE CONCAT ADD FILE
PATH: /ft/resources/client/Flyout.js
*/
var Flyout = function() {
	
};

Flyout.clearVisibleFlyouts = function() {
	if (!this.visibles) {
		return;
	}
	for (var i=0; i<this.visibles.length; i++) {
		this.visibles[i].hide();
	}
	this.visibles = [];
};

Flyout.addVisibleFlyout = function(flyout) {
	this.visibles = this.visibles || [];
	this.visibles.push(flyout);
};


Flyout.prototype.CSS_HIDDEN = "wsodHidden";
Flyout.prototype.CSS_CONTAINER = "flyout";
Flyout.prototype.CSS_SELECTED = "selected";
Flyout.prototype.CSS_SHADOW = "shadow";
Flyout.prototype.CSS_SUB = "sub";

Flyout.prototype.POSITION_BELOW = "below";
Flyout.prototype.POSITION_RIGHT = "right";

Flyout.prototype.addTarget = function(el) {
	this.getShowEvent().addElement(el);
};

Flyout.prototype.getEventManager = function() {
	var em = new EventManager();
	this.getEventManager = function() {
		return em;
	};
	return this.getEventManager();
};

Flyout.prototype.getShowEvent = function() {
	var showEvent = Events.add(null, "mouseup", this.show, this);
	this.getShowEvent = function() {
		return showEvent;
	};
	return this.getShowEvent();
};


Flyout.prototype.getHideEvent = function() {
	var hideEvent = Events.add(null, "mouseout", this.hide, this, null, 1000);
	this.getHideEvent = function() {
		return hideEvent;
	};
	return this.getHideEvent();
};

Flyout.prototype.getPageClickEvent = function() {
	var e = Events.add(null, "mouseup", this.hide, this);
	this.getPageClickEvent = function() {
		return e;
	};
	return this.getPageClickEvent();
};

Flyout.prototype.getMouseOver = function() {
	var mouseOver = Events.add(null, "mouseover", this.show, this);
	this.getMouseOver = function() {
		return mouseOver;
	};
	return this.getMouseOver();
};

Flyout.prototype.getParent = function() {
	return WSDOM.Element.get("wsod");
};

Flyout.prototype.setParent = function(el) {
	this.getParent = function() {
		return el;
	};
};

Flyout.prototype.getFlyoutParent = function() {
	return null;
};

Flyout.prototype.setFlyoutParent = function(flyout) {

	this.setParent(flyout.getContainer());
	this.setPositionTarget(flyout.getContainer());
	
	this.getFlyoutParent = function() {
		return flyout;
	};
	
	WSDOM.Element.addClass(this.getFrame(), this.CSS_SUB);
};

Flyout.prototype.getFrame = function() {
	var frame = WSDOM.Element.create('div', { className: this.CSS_CONTAINER }, [
		WSDOM.Element.create("iframe", { "class": this.CSS_SHADOW, src: "javascript:false;", frameborder: 0 }),
		WSDOM.Element.create("div", { "class": this.CSS_SHADOW }),
		WSDOM.Element.create("ul")
	], this.getParent());
	
	WSDOM.Element.addClass(frame, this.CSS_HIDDEN);
	
	this.getFrame = function() {
		return frame;
	};
	
	return this.getFrame();
};

Flyout.prototype.getShim = function() {
	return this.getFrame().childNodes[0];
};

Flyout.prototype.getShadow = function() {
	return this.getFrame().childNodes[1];
};

Flyout.prototype.getContainer = function() {
	var container = this.getFrame().childNodes[2];
	this.getMouseOver().addElement(container);
	
	this.getContainer = function() {
		return container;
	};
	
	return this.getContainer();
};

Flyout.prototype.setData = function(data) {
	this.data = data;
	this.items = [];
	this.itemsById = {};
	
	var container = this.getContainer();
	
	WSDOM.Element.removeChildNodes(container);

	for (var i=0; i<data.length; i++) {
		
		var flyoutItem = new FlyoutItem(this);
		flyoutItem.setData(data[i]);
		flyoutItem.isLast((i == data.length-1));
		this.items.push(flyoutItem);
		
		if (data[i].id) {
			this.itemsById[data[i].id] = flyoutItem;
		}
		
	};
	
};

Flyout.prototype.isVisible = function() {
	return !WSDOM.Element.hasClass(this.getFrame(), this.CSS_HIDDEN);
}

Flyout.prototype.show = function(e, el) {	
	e.cancel();
	
	this.getHideEvent().clearDelayTimeouts();
	
	if (this.isVisible()) {	
	
		if (el == this.currentTarget) {
			this.currentTarget = null;
			this.hide();
		}
		
		return;
	}
	
	if (!this.getFlyoutParent()) {
		this.currentTarget = el;
		Flyout.clearVisibleFlyouts();
		Flyout.addVisibleFlyout(this);
	}
	
	var frame = this.getFrame();
	
	this.getHideEvent().addElement([frame, el]);
	this.getPageClickEvent().addElement(document);
	
	this.deselectAll();
	
	WSDOM.Element.removeClass(frame, this.CSS_HIDDEN);
	this.position(el);
};

Flyout.prototype.position = function(elTarget) {
	var frame = this.getFrame();
	WSDOM.Element.setStyle(frame, "visibility: hidden");
	
	var target = this.getPositionTarget() || elTarget;
	
	var targetPos = WSDOM.Element.getXY(target);
	var targetSize = WSDOM.Element.getSize(target);
	
	var size = WSDOM.Element.getSize(this.getFrame());
	var offset = WSDOM.Element.getXY(this.getFrame().offsetParent) || { x: 0, y: 0 };
	
	var x = targetPos.x - offset.x;
	var y = targetPos.y - offset.y;
	
	if (this.POSITION_BELOW == this.getPositionStyle()) {
		y += targetSize.height;
	}
	else if (this.POSITION_RIGHT == this.getPositionStyle()) {
		x += targetSize.width;
		y = this.checkVerticalPos(y, elTarget, size, offset);
	}
	
	WSDOM.Element.setXY(
		this.getFrame(), x, y
	);
	
	this.sizeShim(size);
	
	WSDOM.Element.setStyle(frame, "visibility: visible");
};

Flyout.prototype.sizeShim = function(size) {
	// because i can't figure out how to make IE6 do this on its own
	// with the magic of CSS (it's ignoring height 100%)
	// laaaaaaame
	WSDOM.Element.setSize(this.getShim(), size.width, size.height);
	WSDOM.Element.setSize(this.getShadow(), size.width, size.height);
};

Flyout.prototype.checkVerticalPos = function(y, elTarget, size, offset) {
	var elTargetSize = WSDOM.Element.getSize(elTarget);
	var elTargetPos = WSDOM.Element.getXY(elTarget);
	var elTargetBottom = elTargetPos.y + elTargetSize.height - offset.y;
	
	if (elTargetBottom > y + size.height) {
		y = elTargetBottom - size.height;
	}
	
	return y;
};

Flyout.prototype.getPositionStyle = function() {
	return this.POSITION_BELOW;
};

Flyout.prototype.setPositionStyle = function(style) {
	this.getPositionStyle = function() {
		return style
	};
};

Flyout.prototype.getPositionTarget = function() {
	return null;
};

Flyout.prototype.setPositionTarget = function(el) {
	this.getPositionTarget = function() {
		return el;
	};
};

Flyout.prototype.hide = function(e, el) {
	WSDOM.Element.addClass(this.getFrame(), this.CSS_HIDDEN);
	
	//frieken ie6
	this.getFrame().style.visibility = "hidden";
	
	this.hideChildren();
	
	this.getHideEvent().removeAllElements();
	this.getPageClickEvent().removeAllElements();
};

Flyout.prototype.deselectAll = function() {
	for (var i=0; i<this.items.length; i++) {
		WSDOM.Element.removeClass(this.items[i].getContainer(), this.CSS_SELECTED);
	};
}

Flyout.prototype.hideChildren = function(item) {
	for (var i=0; i<this.items.length; i++) {
		if (item == this.items[i]) {
			continue;
		}
		
		WSDOM.Element.removeClass(this.items[i].getContainer(), this.CSS_SELECTED);
		
		if (this.items[i].child) {
			this.items[i].child.hide();
		}
	}
};

Flyout.prototype.hover = function(item) {
	this.getHideEvent().clearDelayTimeouts();
	this.deselectAll();
	this.hideChildren();

	var parent = this;
	while (parent = parent.getFlyoutParent()) {
		parent.getHideEvent().clearDelayTimeouts();
	}
};

Flyout.prototype.onSelect = function() {
	var event = this.getEventManager().add("flyoutItemSelected");
	this.onSelect = function() {
		return event;
	};
	
	this.onSelect().addListener(function(e, item) {
		
		
		if (item.child) {
			// we do nothing on click
			// prevent propagation?
			return;
		}
		
		//console.log("firing", e, item)
		this.hide();
		
		// do I have to fire the handlers all the way up the tree?
		//var parent = this;
		//while (parent = parent.getFlyoutParent()) {
		
		var parent = this.getFlyoutParent();
		
		if (parent) {
			parent.onSelect().fire(item);
			parent.hide();
		}
		//}
			
	}, this);
	
	return this.onSelect();
};





// ------------------------------------------------------------
// ------------------------------------------------------------
// ------------------------------------------------------------


var FlyoutItem = function(flyout) {
	if (!flyout) {
		throw new Error("FlyoutItem Constructor: flyout is required");
	}
	this.flyout = flyout;
};

FlyoutItem.prototype.CSS_HAS_CHILD = "icon icon-arrow-right";
FlyoutItem.prototype.CSS_LAST = "last";

FlyoutItem.prototype.getHoverEvent = function() {
	var hover = this.flyout.getEventManager().add(null, "mouseover", this.hover, this);
	this.getHoverEvent = function() {
		return hover;
	};
	return this.getHoverEvent();
};

FlyoutItem.prototype.hover = function(e) {
	e.cancel();
	
	this.flyout.hover(this);
	
	if (this.child) {
		this.child.getShowEvent().fire(this.getContainer());
		//this.show()
	}
	
	WSDOM.Element.addClass(this.getContainer(), this.flyout.CSS_SELECTED);
	
	this.flyout.sizeShim(WSDOM.Element.getSize(this.flyout.getContainer()))
};

FlyoutItem.prototype.getSelectEvent = function() {
	var event = this.flyout.getEventManager().add(null, "click", this.select, this);
	
	this.getSelectEvent = function() {
		return event;
	}
	
	return this.getSelectEvent();
};

FlyoutItem.prototype.getLabel = function() {
	return null;
};

FlyoutItem.prototype.setLabel = function(label) {
	this.getLabel = function() {
		return label;
	};
	
	this.getContainer().innerHTML = label;
};

FlyoutItem.prototype.getValue = function() {
	return null;
};

FlyoutItem.prototype.setValue = function(value) {
	this.getValue = function() {
		return value;
	}
	
	if (undefined === value) {
		value = "";
	}
	
	this.getContainer().setAttribute("value", value);
};

FlyoutItem.prototype.getContainer = function(data) {
	var container = WSDOM.Element.create("li", null, null, this.flyout.getContainer());
	
	// this belongs somewhere else
	if (data.data) {
		for (var i in data.data) {
			container.setAttribute("data." + i, data.data[i]);	
		}
	}
	
	// this belongs somewhere else
	if (data.css) {
		WSDOM.Element.addClass(container, data.css);
	}
	
	
	this.getHoverEvent().addElement(container);
	this.getSelectEvent().addElement(container);
	
	this.getContainer = function() {
		return container;
	};
	
	this.setLabel(data.label);
	this.setValue(data.value);
	
	return this.getContainer();
};

FlyoutItem.prototype.setData = function(data) {
	this.data = data;
	
	var container = this.getContainer(data);
	
	if (this.child) {
		WSDOM.Element.remove(this.child.getFrame());
		WSDOM.Element.removeClass(container, this.CSS_HAS_CHILD);
		delete this.child;
	}
	
	
	if (data.children) {
		//console.log(data)
		var f = new Flyout();

		f.setFlyoutParent(this.flyout);
		f.setPositionStyle(f.POSITION_RIGHT);

		f.setData(data.children);
		
		f.addTarget(container);
		
		// this belongs somewhere else
		if (data.css) {
			WSDOM.Element.addClass(f.getFrame(), data.css);
		}
		
		WSDOM.Element.addClass(container, this.CSS_HAS_CHILD);
		
		this.child = f;
	}
};

FlyoutItem.prototype.isLast = function(isLast) {
	WSDOM.Element.switchClass(this.getContainer(), this.CSS_LAST, isLast);
};

FlyoutItem.prototype.select = function() {
	this.flyout.onSelect().fire(this);
};
	